    ;^Z80^
    ; modem control information
    ; Coleco 300 baud direct connect modem
    ;      4/5/84
    ;
    ; Data port = 05EH
    ; Control port = 05FH
    ;
    ; Useful equates
    DATAP   EQU     05EH
    CTRLP   EQU     05FH
    ;
    ;     Mode instruction values
    NULL    EQU     080H
    ;
    ; To generate mode byte, select one from column A
    ; and one from column B. See your INTEL dealer for optional
    ; accessories. Add these all together, and spit 'em out.
    ; Chef's suggestion: DEFAULT EQU  STPB1+BITS7+PEN+EP
    ;                    [7 bits, 1 stop bit, even parity]
    ;
    ; column A
    STPB1   EQU     040H    ; 1 stop bit
    STPB15  EQU     080H    ; 1 1/2 stop bits
    STPB2   EQU     0C0H    ; 2 stop bits
    ; column B
    BITS5   EQU     000H    ; 5 bits/char
    BITS6   EQU     004H    ; 6 bits/char
    BITS7   EQU     008H    ; 7 bits/char
    BITS8   EQU     00CH    ; 8 bits/char
    ; optional accessories
    EP      EQU     020H    ; 1=even parity, 0=odd
    PEN     EQU     010H    ; 1=enable parity, 0=disable
    ; required options
    ;  ---> The only one applicable here is 300 baud!!!  <---
    X64     EQU     003H    ; 64x clock rate [300 baud]
    ;
    ; the assumed default value
    DEFAULT EQU     STPB2+BITS8     ; 8 bits, 2 stops, no parity
    ; Command instruction values
    ;
    ; Select desired functions, then add to get command byte
    ;
    EHM     EQU     080H    ; 1=enter hunt mode [no effect on async op]
                            ; [same as NULL]
    IR      EQU     040H    ; 1=internal reset
    RTS     EQU     020H    ; 1=set RTS to 0, enable transmitter
    ER      EQU     010H    ; 1=reset error conditions
    SBRK    EQU     008H    ; 1=send break [TXD goes "low"]
    RXE     EQU     004H    ; 1=receive enable
    TXE     EQU     001H    ; 1=transmit enable
    DTR     EQU     002H    ; set DTR to 0, seize phone line
    ;
    ; Note this important stuff  |
    ;                            V
    ; 1:    If DTR ever gets set to 0, [SZ or preset goes low] then
    ;       modem goes to originate mode.
    ; 2:    Once line is seized, DTR is set to 1, ^SZ is low, so
    ;       7474 has preset=H, data=L,.html clear=H. In this condition,
    ;       toggling clock [RTS or ^TXE] will change to answer mode
    ;
    ; So what?
    ;
    ; With phone hung up, modem is in originate mode. To seize line,
    ; set DTR and RTS to 1 [this drops ^SZ]. This grabs phone line
    ; and enables carrier.
    ; To switch to answer mode, set RTS to 0 then back to 1.
    ;
    ;  STATUS BITS
    ;
    TXRDY   EQU     0               ; indicates transmitter empty
    RXRDY   EQU     1               ; indicates valid character ready
    TXMT    EQU     2               ; indicates buffer empty
    P_ERR   EQU     3               ; set when parity error detected
    OE      EQU     4               ; set on overrun error
    FE      EQU     5               ; set on framing error
    BD      EQU     6               ; set when break detected
    DSR     EQU     7               ; indicated carrier detect
    CD      EQU     DSR
    ;
    ; data areas
    C_DATA  DEFB    NULL            ; current 8251 command
    ;
    ; global subroutine names
    ;
            GLOBAL  DELAY,DIAL,SEIZE,ANSMOD
            GLOBAL  HANGUP,CD_STAT,U_STAT
            GLOBAL  CHR_IN,CHR_OUT,M_INIT
            GLOBAL  SET_UART
    ;
    ;***************************************************************************
    ; SUBROUTINES
    ;***************************************************************************
    ;
    ; Wait 10 msec
    DELAY:
            PUSH            AF              ; [3 uSEC]
            PUSH            BC              ; [3]
            PUSH            DE              ; [3]
            PUSH            HL              ; [3]
            LD              HL,0            ; [2.5]
            LD              DE,0            ; [2.5]
            LD              BC,1590
    ; with all the pushes and pops, we've used about 32 uSEC.
    ; we need a total of 2,000 LDIRs less 7
    ; BUT this is off a bit, so we have to go about 18% faster
            LDIR
            POP             HL              ; [3]
            POP             DE              ; [3]
            POP             BC              ; [3]
            POP             AF              ; [3]
            RET
    ;
    ; *************************************
    ; DIAL DIGIT
    ;  ENTRY: A=ASCII DIGIT 0-9
    ;  EXIT:  NONE
    DIAL:
            PUSH            AF
            PUSH            BC
            PUSH            DE
            PUSH            HL
    ;thaT
            CP              'P'
            JR              Z,PAUSE
            CP              'p'
            JR              Z,PAUSE
            CP              ','             ;Comma=pause
            JR              Z,PAUSE
            CP              '9'+1
            JR              NC,BAD_DGT      ;if character >= ':' then we lose
            CP              '0'
            JR              C,BAD_DGT               ;if char < '0' then we lose
            JR              NZ,CHAR_OK
    ; an ASCII 0 means 10 pulses, so start with '9'+1
            LD              A,'9'+1
    CHAR_OK:
            SUB             '0'
            LD              B,A                     ;number of pulses is now in B
    ;
    PLSE_LP:
    ; All dialing is in originate mode. We assume that the line was
    ; already seized.
    ;
    ; unseize line for 18/29 of the 100 ms pulse time [60 ms]
            LD              A,RXE+TXE+RTS
            OUT             (CTRLP),A       ;PICK UP
            PUSH            BC
            LD              B,6
    PLP1:
            CALL            DELAY
            DJNZ            PLP1
            POP             BC
    ; seize line for 11/29 of the 100 ms pulse time [40 ms]
            LD              A,RXE+TXE+RTS+DTR
            OUT             (CTRLP),A       ;PICK UP
            PUSH            BC
            LD              B,4
    PLP2:
            CALL            DELAY
            DJNZ            PLP2
            POP             BC
    ;
    ; next pulse
            DJNZ            PLSE_LP

            LD              (C_DATA),A              ;SAVE LAST VALUE
    ;
    ; delay between digits
    PAUSE:
            LD              B,80                    ;800 ms
    PLP3:
            CALL            DELAY
            DJNZ            PLP3
    BAD_DGT:
            POP             HL
            POP             DE
            POP             BC
            POP             AF
            RET

    ; ***************************
    ; SEIZE LINE
    ;  ENTRY: A=0 FOR ORIGINATE MODE
    ;         A-0FFH FOR ANSWER MODE
    ;  EXIT:  CARRIER IS ENABLED
    ;         REGS A & C GET TRASHED
    SEIZE:
            LD              C,A             ; SAVE
            CALL            HANGUP
            LD              A,RXE+TXE+RTS+DTR
            LD              (C_DATA),A      ; SAVE LAST COMMAND
            OUT             (CTRLP),A       ; PICK UP
            LD              A,C
            OR              A
            RET             Z               ; ALL DONE
    ; *****************
    ; ANSWER MODE
    ; SWITCHES MODEM DIRECTLY TO ANSWER MODE
    ; Assumes modem off-hook
    ;    EXIT:  REG A TRASHED
    ANSMOD:
    ; ONE FULL PULSE OF RTS LINE
            LD              A,RXE+TXE+DTR
            OUT             (CTRLP),A
            LD              A,RXE+TXE+RTS+DTR
            LD              (C_DATA),A      ; SAVE LAST COMMAND
            OUT             (CTRLP),A
            RET
    ; **********************
    ; HANGUP
    ;    HANGS UP PHONE. TRASHES REG A
    ;
    HANGUP:
            LD              A,RXE+TXE+RTS
            LD              (C_DATA),A      ; SAVE LAST COMMAND
            OUT             (CTRLP),A       ; HANG UP PHONE AND SWITCH TO ORIGINATE MODE
            RET
    ; **********************
    ; CARRIER_STATUS
    ;   READS STATUS FROM UART AND MODEM CHIP
    ;       ENTRY:
    ;       EXIT:   NZ=CARRIER DETECT
    ;               Z=NO CARRIER
    CD_STAT:
            IN              A,(CTRLP)
            BIT             DSR,A
            RET
    ; **********************
    ; UART_STATUS
    ;   CHECKS UART FOR ERRORS AND/OR DATA
    ;       ENTRY:
    ;       EXIT:   NC=NO ERRORS
    ;               C=DATA ERROR
    ;               NZ=CHARACTER PRESENT
    ;               Z=NO CHARACTER
    U_STAT:
            IN              A,(CTRLP)
            OR              A
            JR              URT_CMN
    URT_ERR:
            SCF
    URT_CMN:
            BIT             RXRDY,A
            RET
    ; ************************
    ; CHARACTER_IN
    ;       ENTRY:
    ;       EXIT:   A=CHARACTER FROM MODEM
    ;               NC=NO ERROR
    ;               C=DATA ERROR

    CHR_IN:
            CALL            U_STAT
            JR              Z,CHR_IN        ;WAIT FOR CHARACTER
            IN              A,(DATAP)
            OR              A
            RET
    CHR_ERR:
            LD              A,(C_DATA)
            OR              ER              ;clear for errors
            OUT             (CTRLP),A
            XOR             ER              ;disable error reset
            OUT             (CTRLP),A
            LD              A,0
            RET
    ; *********************
    ; CHARACTER_OUT
    ;       ENTRY:  A=CHARACTER TO TRANSMIT
    ;       EXIT:   NONE
    CHR_OUT:
            PUSH            AF
    ; WAIT UNTIL TRANSMITTER CLEAR
    CHR_L1:
            IN              A,(CTRLP)
            BIT             TXMT,A
            JR              Z,CHR_L1
    ; DUMP THE SUCKER OUT
            POP             AF
            OUT             (DATAP),A
            RET
    ; *************************
    ; MODEM_INIT
    ;   PRESETS MODEM TO KNOWN INITIAL STATE
    M_INIT:
            LD              A,NULL
            LD              (C_DATA),A      ; fool it into hanging up
            LD              A,DEFAULT       ; desired default value

    ; *************************
    ; SET_UART
    ;   SETS UART TO DESIRED STOP BITS, PARITY, ETC
    ;       ENTRY:  A=stop bits + bits/char + parity
    ;       EXIT:   NONE
    SET_UART:
            OR              X64             ; clock rate is always 64x
            PUSH            AF
            LD              A,080H
            OUT             (CTRLP),A
            OUT             (CTRLP),A       ; get the 8251's attention
            LD              A,040H
            OUT             (CTRLP),A
            POP             AF
            OUT             (CTRLP),A       ; dump the init value out
            LD              A,(C_DATA)
            OUT             (CTRLP),A
            RET